
/*
 *
 * STEPDOS + LCD
 * by Erik van den Boogert
 *
 * Version 1.6, Revision date 2009-09-01, 10:00h
 * Filename: StepDos_V17
 *
 * Modification in version V17:
 * The PumpGate was changed from active HIGH to active LOW.
 * This mofication overcomes the safety and initialization problem that the pumps start running when the interface cables
 * are not connected to the STEPDOS controller and overcome the problem of starting the pumps during an Arduino reset.
 */

//--- Conditionals ---------------------------------------------------------------------------------------------------------------------------------------------
//    Note! define conditionals do not work properly in conjunction with #include directives in the Arduino IDE!

//#define DEBUGSERIAL

//--- Include files --------------------------------------------------------------------------------------------------------------------------------------------

#include <LiquidCrystal.h>
#include <stdio.h>
#include <Messenger.h>


LiquidCrystal lcd(7, 8, 9, 3, 4, 5, 6); // Arguments: (rs,rw,e,d4,d5,d6,d7)
const int LCDRows = 2;
const int LCDCols = 16;

//--- Instantiate Messenger object with the default separator (the space character)
Messenger message = Messenger(); 

//--- Const declarations ---------------------------------------------------------------------------------------------------------------------------------------
//
long                    BaudRate               = 9600; // BaudRate = 9600 Baud, Note, Baudrate MUST be 9600 baud, 
                                                       // since the STEPDOS pumps do not accept faster baudrates!
unsigned long           TimeoutDebounce        = 250;  // Debounce interval in ms for the ButtonMode button
unsigned long           TimeoutPumpFault       = 1000; // Debounce interval in ms for the PumpFault sense lines

char displaystring[10];

//
//--- Hardware output bits associations:
int                     EnableCom[4]           = {10,11,12,13}; //--- EnableCom drives the TS-drivers to switch between COM-ports
int                     PumpGate[3]            = {14,15,16};    //--- PumpGate drives the TTL signals to START/STOP the pump motors

//
//--- Hardware input bits associations: 
int                     ButtonMode             = 2;             //--- ButtonMode is the grey button, sequencing over the COM-ports when pressed
int                     PumpFault[3]           = {17,18,19};    //--- PumpFault senses the PumpFault status bit


// --- Var declarations ----------------------------------------------------------------------------------------------------------------------------------------

//--- declare SerialInChr to receive the protocol stream
byte                    SerialInChr;

//--- declare general time variables for timing
unsigned long           TimeNow                 = 0;

//--- declare PumpIndex variable to assign the appropriate COM-port
unsigned int            PumpIndex               = 0;

//--- declare a time varable to store the time when the PumpGate[] was set and to compare with TimeNow,
//    this way PumpGate[] can be implemented as a pulse with a desireded fixed pulseLength
unsigned long           TimeOnSet_PumpGate[3]   = {0, 0, 0};


//--- declare a time varable to store the time for PumpTime[] to remain active,
//    PumpTime[] is be implemented as a pulsewith with a desired parametic value
volatile unsigned int   PumpTime[3]             = {1000, 1000, 1000};


//--- declare a varable to store the pump flowrate percentage in PumpFlow[],
//    PumpFlow[] is be implemented as a flowrate percentage with a desired parametic value
//    this way the delivered volume amount is implemented as a product of PumpTime[] * PumpFlow[]: 
//    Note: The intergral product of PumpTime[] * PumpFlow[] = delivered volume amount (assuming appropriate calibration)
volatile unsigned int   PumpFlow[3]             = {10000, 10000, 10000};


//--- declare bit state booleans to find out if input values have changed since the last looptest
boolean                 State_ButtonMode        = LOW;
boolean                 Prev_State_ButtonMode   = LOW;
boolean                 State_PumpFault[3]      = {LOW, LOW, LOW};
boolean                 Prev_State_PumpFault[3] = {LOW, LOW, LOW};


//--- declare debouncing varables for each input bit. 
//    Storing the moment in time when the edge was detected and set a boolean Debouncing_Input[n]
unsigned long           TimeOnSet_ButtonMode    = 0;
boolean                 Debouncing_ButtonMode   = false;
unsigned long           TimeOnSet_PumpFault[3]  = {0, 0, 0};
boolean                 Debouncing_PumpFault[3] = {false, false, false};
boolean                 Activated_PumpGate[3]   = {false, false, false};

boolean                 EnableSerialEcho        = false;

//--- Message Parser ------------------------------------------------------------------------------------------------------------------------------------------

int MessageCommand = 0; //--- MessageCommand is a global variable containing the primary command as an integer
long MessageParam  = 0; //--- MessageParam is a global variable containing the optional (single) parameter of a command as an integer


//--- MessageExecute implements the command protocol and calls its associated functional STEPDOS methods
//    Note: See the switch code below to know the correct command protocol syntax
//    Note: Each command must be terminated by a <CR> and parameters must be separated by a <SPACE>
//
//          E.g. 1<CR>         -> STARTs Pump[1] for a predefined time interval as set in FlowTime[0] (Pump[1])
//          E.g. 2<CR>         -> STARTs Pump[2] for a predefined time interval as set in FlowTime[1] (Pump[2])
//          E.g. 3<CR>         -> STARTs Pump[3] for a predefined time interval as set in FlowTime[2] (Pump[3])
//
//          E.g. 11<CR>         -> STARTSs Pump[1] (remains actived!)
//          E.g. 12<CR>         -> STARTSs Pump[2] (remains actived!)
//          E.g. 13<CR>         -> STARTSs Pump[3] (remains actived!)
//
//          E.g. 21<CR>         -> STOPs Pump[1]
//          E.g. 22<CR>         -> STOPs Pump[2]
//          E.g. 23<CR>         -> STOPs Pump[3]
//
//          E.g. 31 1050<CR>  -> Sets FlowTime[0] (Pump[1]) to 1050 ms
//          E.g. 32 50<CR>    -> Sets FlowTime[1] (Pump[2]) to a minumum of 50 ms
//          E.g. 33 16383<CR> -> Sets FlowTime[2] (Pump[3]) to a maximum 16383 ms
//
//          E.g. 41 9800<CR>  -> Sets FlowRate[0] (Pump[1]) to 98.00% of 30 ml/min
//          E.g. 42 0<CR>     -> Sets FlowRate[1] (Pump[2]) to a minumum of 0.00% of 30 ml/min
//          E.g. 43 10000<CR> -> Sets FlowRate[2] (Pump[3]) to a maximum of 100.00% of 30 ml/min
//
//          E.g. -1<CR>         -> Disable status echo over serial port (default status after bootup)
//          E.g. -2<CR>         -> Enable status echo over serial port
//
//    Note: In practice preferrably use the FlowTime commands to adjust/calibrate the delivered volume amount.
//          FlowTime is purely internal in the uC and does not require communication with the STEPDOS pump,
//          FlowRate instead does require communication with the STEPDOS pump. This takes some time, but might 
//          fail and failure might remain unnoticed
//
void messageExecute() {
  //
  switch (MessageCommand) {
    //
    //--- Quick START/STOP commands for the pump motors -------------------------
    //
    case -1:
      { EnableSerialEcho = false; break; }
    case -2:
      { EnableSerialEcho = true; break; }
    //
    //--- START the pumps (PumpGate internally timed) ---------------------------  
    case 1:
      { Start_PumpGate(0); break; }
    case 2:
      { Start_PumpGate(1); break; } 
    case 3:
      { Start_PumpGate(2); break; }
    //
    //--- START the pumps (using PumpGate level dependent ------------------------
    case 11:
      { Set_PumpGate(0, LOW); break; }
    case 12:
      { Set_PumpGate(1, LOW); break; }
    case 13:
      { Set_PumpGate(2, LOW); break; }
    //
    //--- STOP the pumps (using PumpGate level dependent ------------------------
    case 21:
      { Set_PumpGate(0, HIGH); break; } 
    case 22:
      { Set_PumpGate(1, HIGH); break; } 
    case 23:
      { Set_PumpGate(2, HIGH); break; } 
    //
    //--- Set PumpTime ---------------------------------------------------------- 
    case 31:
      { 
      Set_PumpTime(0, MessageParam); 
      break; 
      }
    case 32:
      { 
      Set_PumpTime(1, MessageParam); 
      break; 
      }
    case 33:
      { 
      Set_PumpTime(2, MessageParam); 
      break; 
      }
    //
    //--- Set & Program/Upload PumpFlow ------------------------------------------  
    case 41:
      { 
      //--- Set PumpFlow[0] = newPumpFlowParam 
      Set_PumpFlow(0, MessageParam);
      //--- Switch COM-port to COM[1] and connect to Pump[1] 
      Set_PumpIndex(1);
      //--- Attempt uploading PumpFlow into the STEPDOS pump
      Program_PumpFlow(0);
      //--- Return to COM[0] for continued PC-communication  
      Set_PumpIndex(0);
      break; 
      }
    case 42:
      { 
      Set_PumpFlow(1, MessageParam); 
      Set_PumpIndex(2);
      Program_PumpFlow(1); 
      Set_PumpIndex(0);
      break; 
      }
    case 43:
      { 
      Set_PumpFlow(2, MessageParam); 
      Set_PumpIndex(3);
      Program_PumpFlow(2);  
      Set_PumpIndex(0);
      break; 
      }
    default: 
      { // if nothing else matches, do the default, default is optional
      }
  }
}


//--- Create the callback function for Messager as command parser for PC-commands received over the COM[0]/USB RS232C port
//    Note: this callback procedure is designed according to the Messanger library syntax
void messageReady() {
  //
  //--- Provide a MessageError flag to keep track of invalid number of parameters in the protocol syntax
  boolean MessageError = false; 
  //
  //--- Get the first parameter, which is interpreted as the primary command identifier
  if ( message.available() ) { MessageCommand = message.readInt(); } else { MessageError = true; }
  //
  //--- Primary commands < 30 are preserved for quick commands, which do not take additional parameters
  //    Primary commands >=30 expect one parameter as integer
  if ( MessageCommand >= 30 ) {
  if ( message.available() ) { MessageParam = message.readInt(); } else { MessageError = true; }}
  //     
  // Loop through all the additional available elements of the message
  while ( message.available() ) 
    {
    //---Read additional MessageParams in the message; these imply syntax violations and may be dropped, 
    //  since multi parameter commands do not (yet) exist in the current protocol definition.
    message.readInt();
    MessageError = true; 
    }
  //
  //--- Execute the command if no systax error was detected  
  if ( !MessageError ) { messageExecute(); }   
}


// --- Setup method --------------------------------------------------------------------------------------------------------------------------------------------

void setup()
{
  int index = 0;
  //
  //--- begin the serial communication
  Serial.begin(BaudRate);
  //
  //--- set appropriate bits for output, EnableCom[] bits
  for (index = 0; index < 4; index = index + 1)
  {
    pinMode(EnableCom[index], OUTPUT);
    digitalWrite(EnableCom[index], HIGH); //--- EnableCom[] is active LOW
  }
  //
  //--- set appropriate bits for output, PumpGate[] bits
  for (index = 0; index < 3; index = index + 1)
  {
    pinMode(PumpGate[index], OUTPUT);
    digitalWrite(PumpGate[index], HIGH);  //--- PumpGate[] is active LOW
  }
  //
  //--- set appropriate bits for input and enable internal uC pull-up resistor, ensuring default HIGH input
  pinMode(ButtonMode, INPUT); 
  digitalWrite(ButtonMode, HIGH);  //--- ButtonMode is active LOW
  //
  //--- set appropriate bits for input, PumpFault[] bits
  for (index = 0; index < 3; index = index + 1)
  {
    pinMode(PumpFault[index], INPUT); 
    digitalWrite(PumpFault[index], HIGH);  //--- PumpFault[] is active LOW
  }
  //
  //--- initialize LCD-display
  //
  lcd.begin(LCDRows, LCDCols);
  display_init();
  lcd.setCursor(0, 0); lcd.print("Ready STEPDOS03");   
  lcd.setCursor(0, 1); lcd.print("V1.7 2009/09/01");
  //
  //--- Initialize COM-port to 0 (PC communictaion, not any of the 3 pumps)
  SerialEcho_Initialized();
  Set_PumpIndex(0);
  //
  // Attach the callback function to the Messenger
  message.attach(messageReady);
}


//--- Forever Loop method --------------------------------------------------------------------------------------------------------------------------------------

void loop()
{
  byte index = 0;
  
  //--- Get TimeNow as a reference for all Timeout counters for debouncing inputs and output pulseLength
  TimeNow = millis();
  //
  //--- respond to control messages from the PC that were sent to the USB / COM[0] port
  while ( Serial.available() )  message.process(Serial.read () );
  //
  //--- Process the input bits: -------------------------------------------------------------------------------------------------------------------------------
  //    Each input bit is debounced for TimeoutDebounce ms, which means any state change within the debouncing time is ignored.
  //--- Process the ButtonMode sequence; ----------------------------------------
  //
  State_ButtonMode = digitalRead(ButtonMode);
  //--- check for bit state change = egde, but not within debouncing interval
  if ((Prev_State_ButtonMode != State_ButtonMode) & !Debouncing_ButtonMode)
    {
    if (State_ButtonMode == LOW) { OnNext_ButtonMode(); } 
    //
    //--- start debouncing
    TimeOnSet_ButtonMode = millis();
    Debouncing_ButtonMode = true;
    //--- save new previous bit state
    Prev_State_ButtonMode = State_ButtonMode;
    }
  //
  //--- Check for PumpFaults inputs ---------------------------------------------
  //
  for (index = 0; index < 3; index = index + 1)
    {
    State_PumpFault[index] = digitalRead(PumpFault[index]);
    //--- check for bit state change = egde, but not within debouncing interval
    if ((Prev_State_PumpFault[index] != State_PumpFault[index]) & !Debouncing_PumpFault[index])
      {
      //--- respond pumpfault message over TxD depending on edge low-to-high or high-to-low  
      if (State_PumpFault[index] == LOW) { SerialEcho_PumpFault(index); } 
      //
      //--- start debouncing
      TimeOnSet_PumpFault[index] = millis();
      Debouncing_PumpFault[index] = true;
      //--- save new previous bit state
      Prev_State_PumpFault[index] = State_PumpFault[index];
      }
    }    
  //
  //--- Check debouncing status for each input bit -----------------------------------------------------------------------------------------------------------------
  //--- Check debouncing of ButtonMode button -------------------------------------
  if (Debouncing_ButtonMode & ((TimeNow - TimeOnSet_ButtonMode) > TimeoutDebounce)) { Debouncing_ButtonMode = false; }
  //    
  //--- Check debouncing of PumpFault for each pump -------------------------------
  for (index = 0; index < 3; index = index + 1) 
    {
    if (Debouncing_PumpFault[index] & ((TimeNow - TimeOnSet_PumpFault[index]) > TimeoutPumpFault)) { Debouncing_PumpFault[index] = false; }
    }   
  //    
  //--- Check PumpTime timeout status for each pump --------------------------------------
  for (index = 0; index < 3; index = index + 1) 
    {
    //--- Stop_PumpGate(index) when activated and timeout as defined in PumpTime[index] has expired  
    if (Activated_PumpGate[index] & ((TimeNow - TimeOnSet_PumpGate[index]) > PumpTime[index])) { Stop_PumpGate(index); }
    }   
}


//--- STEPDOS specific methods --------------------------------------------------------------------------------------------------------------------------------


//--- The grey button sequences along the PC-control port (COM[0]) and the 3 pumps (COM[1..3])
//    Each time the grey button is pressed, an OnNext_ButtonMode() method is called
void OnNext_ButtonMode()
{
  // 
  //--- increment the active PumpIndex  
  PumpIndex = PumpIndex + 1;
  Set_PumpIndex(PumpIndex); //--- Note: Set_PumpIndex interally checks the range of PumpIndex to stay within {0 .. 3}
  //
  //--- report new state on the display
  display_Update(PumpIndex);
} 


//--- Set_PumpIndex changes the TS-driver, which selects and enables to one COM[n] ports
//    COM[0] = PC-communication (= Arduino USB-port)
//    COM[1] = Pump[1]
//    COM[2] = Pump[2]
//    COM[3] = Pump[3]
//
void Set_PumpIndex(int Index)
{
  byte index = 0;
  //
  //--- disable all COM-ports
  for (index = 0; index < 4; index = index + 1) { digitalWrite(EnableCom[index],HIGH); }
  //
  //--- Set active PumpIndex (as the global variable PumpIndex)  
  PumpIndex = Index;
  //
  //--- Do range-checking
  if (PumpIndex < 0) {PumpIndex = 0;}
  if (PumpIndex > 3) {PumpIndex = 0;}
  //--- and enable that COM-port
  digitalWrite(EnableCom[PumpIndex],LOW); // The TS-driver is active LOW
  //
  //--- and report back the new state over the COM-port
  SerialEcho_PumpEnable(PumpIndex);
  //
  //--- preserve a minimum time for the COM-port to stay active
  //    this ensures proper start/stop bit conditions for RS232 communication 
  //    and creates a visible LED-flash for the activated COM-port
  delay(20);
} 


//--- Set_PumpGate(Index) sets/clears the TTL-bit, which STARTs and STOPs the Pump motors
//    Note: Index ranges {0 .. 2} for Pump[1 .. 3]
//    Note: LogicState is active LOW, LOW corresponds to a running pump motor
//
//    Note: In hardware the PumpGate TTL-bit is attached to pin 1 of the RS232C SubD9 connector (relative to GND pin 5)
//
void Set_PumpGate(int Index, boolean LogicState)
{
  //
  //--- Set PumpGate [index] at desired logic state LOW or HIGH
  digitalWrite(PumpGate[Index],LogicState);
  //
  //--- report new state on the display
  display_Update(Index+1);
  lcd.setCursor(0, 0); if (LogicState ) { lcd.print("STOP"); } else { lcd.print("RUN"); }
  //
  //--- and report back the new state over the COM-port
  SerialEcho_PumpGate(Index,LogicState);
}


//--- Start_PumpGate(Index) starts the Pump motor for a preloaded time as defined in FlowTime[Index]
//    Start_PumpGate uses Set_PumpGate
void Start_PumpGate(int Index)
{
  Set_PumpGate(Index, LOW);            //--- Pumpgate is active LOW (since V17)
  TimeOnSet_PumpGate[Index] = TimeNow; //--- Start timing by marking the start moment
  Activated_PumpGate[Index] = true;    //    and marking the activated flag, which enables the timeout delay counter
}


//--- Stop_PumpGate(Index) aborts and stops the Pump motor even in case FlowTime[Index] countdown
//    Stop_PumpGate uses Set_PumpGate
void Stop_PumpGate(int Index)
{
  Activated_PumpGate[Index] = false;   //    clear the activated flag, which disables the timeout delay counter
  Set_PumpGate(Index, HIGH);           //    stop the pump 
}


//--- Program_PumpFlow(Index) attempts to upload a new flowrate into the STEPDOS-03-RC pump
//    Note: The new flowrate is not a parameter of this method, but is implicitly derived from the global variable PumpFlow[Index],
//          which should be preloaded in a proceeding call to the Set_PumpFlow[] method
//    Note: Index ranges {0 .. 2} for Pump[1 .. 3]
//    Note: This method does not perform any verification and is NOT aware if the upload failed for whatever reason (e.g. the pump not being connected)
//    Note: This method implements the required STEPDOS interface protocol and performs VRC checksum calculation
void Program_PumpFlow(int Index)
{
  int index = 0;
  byte VRC = 0;
  char Message[] = "#99RR10000##";
  //                012345678901
  //<STX>=02,address="00",data="RV00023400",<ETX>=03,VRC
  //
  //--- format the STEPDOS protocol message with the PumpFlow[Index] parameter as a 5 digit integer number
  sprintf(Message, "#99RR%5i##", PumpFlow[Index]);
  //
  Message[0] = 2;  //--- override <STX>=02 in corresponding string position 
  Message[10] = 3; //--- override <ETX>=03 in corresponding string position
  //
  //--- Calculate the VRC checksum as an XOR of all bytes in the message, including STX and ETX
  VRC = Message[0] ^ Message[1];
  for (index = 2; index < 11; index = index + 1) { VRC = VRC ^ Message[index]; }
  Message[11] = VRC; //--- override VRC in corresponding string position
  //
  //--- Serial Tx the calculated STEPDOS protocol message
  Serial.println(Message);
}  


//--- Set_PumpFlow(Index, FlowPercentage) sets the global variable FlowRate[Index]
//    Note: Index ranges {0 .. 2} for Pump[1 .. 3]
//    Note: FlowPercentage ranges {0 .. 10000} representing 0.00% .. 100.00% of the STEPDOS03 flowrate (100% = 30 ml/min)
void Set_PumpFlow(int Index, int FlowPercentage)
{
  // 
  //--- Perform range checking for FlowPercentage parameter 
  if (FlowPercentage < 0) { FlowPercentage = 0; }
  if (FlowPercentage > 10000) { FlowPercentage = 10000; }
  //
  //--- Set PumpFlow[Index]
  PumpFlow[Index] = FlowPercentage;
  //
  //--- report new state on the display
  display_Update(Index+1);
}


//--- Set_PumpTime(Index, Timems) sets the global variable FlowTime[Index]
//    Note: Index ranges {0 .. 2} for Pump[1 .. 3]
//    Note: Timems ranges {50 .. 16383} representing 50ms .. 16383ms
void Set_PumpTime(int Index, int Timems)
{
  // 
  //--- Perform range checking for Timems parameter 
  if (Timems < 50) { Timems = 50; }               //--- Timems < 50ms makes no sense
  if (Timems > 16383) { Timems = 16383; } //--- Timems > 16384ms overrides the signed int-range and makes no sense
  //
  //--- Set PumpTime[Index]
  PumpTime[Index] = Timems;
  //
  //--- report new state on the display
  display_Update(Index+1);
}


//--- SerialEcho methods --------------------------------------------------------------------------------------------------------------------------------------


//--- Generic SerialEcho method which may encapsulate protocol overhead
void Serial_Tx(char Message[10])
{
  if (EnableSerialEcho) { Serial.println(Message); }
}


//--- SerialEcho succesfull initialization
void SerialEcho_Initialized()
{
  Serial_Tx("IN[0]");
}


//--- SerialEcho the PumpEnable, which is the TS-Enable for the 4 COM-ports.
//    COM-ports {1,2,3} are reserved for the 3 pumps, COM-port {0} is reserved for PC-communication
void SerialEcho_PumpEnable(byte index)
{
  //
  //--- Report_PumpEnable(index);
  if (index == 0) { Serial_Tx("PE[0]"); }
  if (index == 1) { Serial_Tx("PE[1]"); }
  if (index == 2) { Serial_Tx("PE[2]"); }
  if (index == 3) { Serial_Tx("PE[3]"); }
} 


//--- SerialEcho a PumpFault for any of the 3 pumps
//    Note: PumpFaults are not reported on the LCD-diaplsy
void SerialEcho_PumpFault(byte index)
{
  //
  //--- Report_PumpFault(index);
  if (index == 0) { Serial_Tx("PF[1]"); }
  if (index == 1) { Serial_Tx("PF[2]"); }
  if (index == 2) { Serial_Tx("PF[3]"); }
} 


//--- SerialEcho PumpGate status RUN/STOP
void SerialEcho_PumpGate(byte index, boolean LogicState)
{
  //
  //--- Report_PumpGate(index);
  if ((index == 0) &  LogicState) { Serial_Tx("PG[1]"); } //--- Capital characters represent a RUNning pump
  if ((index == 1) &  LogicState) { Serial_Tx("PG[2]"); }
  if ((index == 2) &  LogicState) { Serial_Tx("PG[3]"); }
  if ((index == 0) & !LogicState) { Serial_Tx("pg[1]"); } //--- Small characters represent a STOPped pump
  if ((index == 1) & !LogicState) { Serial_Tx("pg[2]"); }
  if ((index == 2) & !LogicState) { Serial_Tx("pg[3]"); }
} 


//--- LCD methods ----------------------------------------------------------------------------------------------------------------------------------------------
//
//   The display has 2 lines of 16 characters each
//   0123456789012345     
//   ----------------
// 0|RUN    Tm= 9999|  RUN/STOP              RunTime in milliseconds, ranging {0 .. 16383} ms
// 1|Pump=1 Fw=10000|  Pump ranging {1,2,3}, FlowRate in 100* %, ranging {0 .. 10000} %, corresponding with {0 .. 30.000} ml/min.
//   ----------------


//--- initialize the display without drawing permanent text 
void display_init()
{
  lcd.clear();
}


//--- update the full display
void display_Update(byte PumpIndex)
{
  //--- Only update the display for PumpIndex > 0, since Pump=0 does not exist
  if (PumpIndex > 0) {
  lcd.clear();
  display_PumpIndex(PumpIndex);  //--- Note: PumpIndex counts human {1,2,3}
  display_PumpTime(PumpIndex-1); //--- Note: PumpTime[] is a C-array and counts {0,1,2}
  display_PumpFlow(PumpIndex-1); //--- Note: PumpFlow[] is a C-array and counts {0,1,2}
  }
}


//--- update the PumpIndex on the display
void display_PumpIndex(byte PumpIndex)
{
  //--- format and display the new PumpIndex
  lcd.setCursor(0, 1);
  sprintf(displaystring, "Pump=%1i", PumpIndex);
  lcd.print(displaystring);
}


// update the PumpFlow[PumpIndex] on the display
void display_PumpFlow(byte PumpIndex)
{
  //--- format and display the new PumpFlow
  lcd.setCursor(7, 1);
  sprintf(displaystring, "Fw=%5i", PumpFlow[PumpIndex]);
  lcd.print(displaystring);
}


// update the PumpTime[PumpIndex] on the display
void display_PumpTime(byte PumpIndex)
{
  //--- format and display the new PumpTime
  lcd.setCursor(7, 0);
  sprintf(displaystring, "Tm=%5i", PumpTime[PumpIndex]);
  lcd.print(displaystring);
}
